/*
 -- =============================================
 ** Author	  :	Paulo Fournier
 ** Create date: 2011-03-18
 ** Description:	A custom marker object for the google maps API v3
 -- =============================================
 */

Ext.namespace('API.Marker');

API.Marker.Mobile = function (options) {

    this.clickable = true; //the if the marker is clickable
    this.container = null; //will contain the marker
    this.shrinked = undefined; //flag to check if the marker is shrinked
    this.hidden = false; //flag to check if the marker is hidden
    this.popingUp = false; //flag to check if the marker is poped up (in front)
    this.keepInFront = false;
    this.isIconVisible = options.isIconVisible === true ? true : false;
    this.marker = null;

    //object that contains the maker in the two
    this.div = {
        normal: null,
        shrinked: null
    };

    //object that contains the marker options
    this.opt = {
        map: null,
        iconid: 1,
        imgUrl: 'image.db.php',
        mobileMarkerType: 'ICON'
    };
    //override options
    for (var key in options){
        this.opt[key] = options[key];
    }

    if (this.opt['lat'] !== null && this.opt['lat'] !== undefined && this.opt['lng'] !== null && this.opt['lng'] !== null)
        this.set('position', new google.maps.LatLng(this.opt['lat'], this.opt['lng']));

    //set the marker template
    this.setTemplate();
    //set the marker map (if it's set)
    if (this.opt.map)
        this.setMap(this.opt.map); //Adds the overlay to the map

    //set a marker to behave like a click area
    this.marker = new google.maps.Marker({
        map: this.getMap(),
        draggable: false,
        position: this.getPosition(),
        flat: true,
        visible: true,
        zIndex: 2,
        icon: this.opt['emptyIcon']
    });
};

/**
 * Define the API.Marker marker as an extention of the google.maps.OverlayView object
 */
API.Marker.Mobile.prototype = new google.maps.OverlayView();


/**
 * Set the marker tempaltes for the diferent shapes
 */
API.Marker.Mobile.prototype.setTemplate = function () {

    Ext.DomHelper.useDom = true;

    //set shapes templates
    this.shape = {
        //the normal template (unshrinked)
        normal: new Ext.DomHelper.createTemplate(
            '<div class="marker-icon-container" style="{style}"></div>' +
            '<div id="mkr-pane-{id}" class="marker-pane' + (Ext.isIE ? ' marker-pane-ie' : '') + '"></div>' +
            '<div id="mkr-pane-txt-{id}" class="marker-text" style="visibility:{txt_visibility};">{alias}</div>' +
            '<img id="mkr-pane-img-{id}" src="{url}" class="marker-image" style="visibility:{img_visibility};" title="{title}">'
        ),
        //the small template (shrinked)
        small: new Ext.DomHelper.createTemplate(
            '<div class="marker-icon-container-shrinked" style="{style}"></div>' +
            '<div id="mkr-pane-shrinked-{id}" class="marker-pane-shrinked' + (Ext.isIE ? ' marker-pane-shrinked-ie' : '') + '"></div>' +
            '<div id="mkr-pane-shrinked-txt-{id}" class="marker-text-shrinked" style="visibility:{txt_visibility};">{alias}</div>' +
            '<img id="mkr-pane-shrinked-img-{id}" src="{url}" class="marker-image-shrinked" style="visibility:{img_visibility};" title="{title}">'
        ),
        //the label template
        label: {
            //unshrinked shape
            normal: new Ext.DomHelper.createTemplate(
                '<div id="mkr-label-{id}">' +
                    '<div id="mkr-label-ball-{id}" class="marker-label-ball ' + (this.shrinked ? 'marker-label-ball-shrinked' : '') + '">&nbsp;</div>' +
                    '<div id="mkr-label-center-{id}" class="marker-label-center ' + (this.shrinked ? 'marker-label-center-shrinked' : '') + '">{alias}</div>' +
                    '<div id="mkr-label-edge-{id}" class="marker-label-edge">&nbsp;</div>' +
                '</div>'),
            //shrinked shape
            small: new Ext.DomHelper.createTemplate(
                '<div id="mkr-label-{id}">' +
                    '<div id="mkr-label-ball-{id}" class="marker-label-ball marker-label-ball-shrinked">&nbsp;</div>' +
                    '<div id="mkr-label-center-{id}" class="marker-label-center marker-label-center-shrinked">{alias}</div>' +
                    '<div id="mkr-label-edge-{id}" class="marker-label-edge">&nbsp;</div>' +
                '</div>')
        }
    };
};

/**
 * Implement this method to initialize the overlay DOM elements.
 * This method is called once after setMap() is called with a valid map.
 * At this point, panes and projection will have been initialized.
 */
API.Marker.Mobile.prototype.onAdd = function () {
    if(this.getMap().getZoom() >= API.Map.ZOOM_OUT_REMOVE_LEVEL && this.shrinked){
        this.unShrink();
    } else {
        this.shrink();
    }

    //add object listeners
    (function (scope) {
        google.maps.event.addListener(scope.marker, 'click', function () {
            scope.click();
        });
        google.maps.event.addListener(scope.marker, 'mouseover', function () {
            scope.toFront();
        });
        google.maps.event.addListener(scope.marker, 'mouseout', function () {
            scope.toBack();
        });
    })(this);
};

/**
 * Function to set the normal DOM object (unshrinked)
 */
API.Marker.Mobile.prototype.setNormalDiv = function () {
    //create DOM element
    this.div_ = document.createElement("div");

    //set style
    this.div_.className = 'marker-pane-container';

    //merge license plate values and show the last 4 chars
    var alias = this.opt.alias.replace(/-/gi, '');
    alias = alias.substring(alias.length - 4, alias.length);

    //set the DIV object
    this.shape.normal.append(this.div_, {
        id: this.opt.id,
        title: this.opt.alias,
        url: this.opt.imgUrl + '?id=' + this.opt.iconid,
        alias: alias,
        txt_visibility: this.isIconVisible ? 'hidden' : 'visible',
        img_visibility: this.isIconVisible ? 'visible' : 'hidden'
    });

    //add DIV overlay to map pane
    this.getPanes().overlayLayer.appendChild(this.div_);

    // set normal div on div object
    this.div.normal = this.div_;
};

/**
 * Function to set the small DOM object (shrinked)
 */
API.Marker.Mobile.prototype.setShrinkDiv = function () {
    //creat the div object tha will contain the marker overlay
    this.div_ = document.createElement("div");

    //set style
    this.div_.className = 'marker-pane-container-shrinked';

    //merge license plate values and show the last 4 chars
    var alias = this.opt.alias.replace(/-/gi, '');
    alias = alias.substring(alias.length - 4, alias.length);

    //set the DIV object
    this.shape.small.append(this.div_, {
        id: this.opt.id,
        title: this.opt.alias,
        url: this.opt.imgUrl + '?id=' + this.opt.iconid,
        alias: alias,
        txt_visibility: this.isIconVisible ? 'hidden' : 'visible',
        img_visibility: this.isIconVisible ? 'visible' : 'hidden'
    });

    //add DIV overlay to map pane
    this.getPanes().overlayLayer.appendChild(this.div_);

    // set shrinked div on div object
    this.div.shrinked = this.div_;
};

/**
 * The will be triggered each time the user clicks on the marker object
 */
API.Marker.Mobile.prototype.click = function (e) {
    //focus the respective mobile on the main grid
    if (API.Fleet && API.Fleet.Grid)
        API.Fleet.Grid.focusMobile(this.opt.id);

    //define as coordenadas do overlay
    var latlng = this.getPosition();

    //check if thr map projection is set
    if (this.getProjection() !== undefined) {
        //Computes the pixel coordinates of the given geographical location in the DOM element that holds the map
        var point = this.getProjection().fromLatLngToContainerPixel(latlng);
        //get map div offset
        var offset = $(this.getMap().getDiv()).offset();
        //show menu on marker position
        this.opt.menu.showAt(point.x + offset.left, point.y + offset.top);
    }
};

/**
 * Force marker to appear on the max front position (z-axis)
 */
API.Marker.Mobile.prototype.toFront = function () {
    if(this.div_ != null){
        //check if this marker is highlighted. Ff not, make it more closer to user (bigger zIndex) to overlap any highlighted marker
        var z = this.keepInFront ? 0 : 2;
        //set the marker zIndex to appear on front
        this.div_.style.zIndex = 7 - z;
        //show the marker label
        this.showLabel();
        //set the label zIndex to appear on front of all other markers except this one
        this.label_.style.zIndex = 6 - z;
    }
};

/**
 * Force marker to go to it's original position (z-axis)
 */
API.Marker.Mobile.prototype.toBack = function () {
    //check if marker is not highlighted before sending it to back
    if (!this.keepInFront && this.div_ != null) {
        //hide the marker label
        this.hideLabel();
        //reset the marker zIndex
        this.div_.style.zIndex = 3;
        //reset the label zIndex
        if(this.label_)
            this.label_.style.zIndex = 3;
    }
};

/**
 * Make the marker to pop up in the main map (The marker will be highlighted)
 */
API.Marker.Mobile.prototype.popup = function () {
    //check if marker is forced expanded
    if (!this.keepInFront) {
        //force marker to go to the front position
        this.toFront();
        //prevent FX overlap with popingUp flag
        if (this.label_ && !this.popingUp) {
            this.popingUp = true;
            //auto fadeout after a few seconds
            (function () {
                //fade out
                Ext.get(this.label_).fadeOut({
                    endOpacity: 0,
                    easing: 'easeOut',
                    duration: 1.5,
                    remove: false,
                    useDisplay: true,
                    block: true,
                    remove: false,
                    scope: this,
                    callback: function () {
                        this.toBack();
                        this.popingUp = false;
                    }
                });
            }).defer(1000, this);
        }
    }
};

/**
 * Show the marker's label
 * if the label is not set, will attempted to create it
 */
API.Marker.Mobile.prototype.showLabel = function () {
    //check if label is set
    if (!this.label_) {
        //creat the div object tha will contain the marker overlay
        this.label_ = document.createElement("div");
        //set the DOM style
        this.label_.className = 'marker-label-container';
        //get mobile alias
        var alias = API.Geral.formatMobileAlias(this.opt.alias, this.opt.license_plate);
        //check max size
        if (alias.length > 15)
            alias = alias.substring(0, 12) + '...';
        //set the DIV object
        if (this.shrinked){
            this.shape.label.small.append(this.label_, {
                id: this.opt.id,
                alias: alias
            });
        } else {
            this.shape.label.normal.append(this.label_, {
                id: this.opt.id,
                alias: alias
            });
        }
        //get map pane
        var panes = this.getPanes(); //obtem os overlays adicionados no mapa
        //add DIV overlay to map pane
        panes.overlayLayer.appendChild(this.label_); //adiciona o novo HTMLDivElement ao mapa
    }
    //update label position
    this.setLabelPosition();
    //show label
    Ext.get(this.label_).show();
};

/**
 * Update the label position based on the marker position
 */
API.Marker.Mobile.prototype.setLabelPosition = function () {
    //check if label is set
    if (this.label_) {
        //set the DIV position
        var latlng = this.getPosition();

        //define as coordenadas do overlay
        var point = this.getProjection().fromLatLngToDivPixel(latlng); //obtem o ponto em pixeis do overlay no document
        //set position
        this.label_.style.left = (point.x - 19) + "px"; //left
        this.label_.style.top = (point.y - 19) + "px"; //top
    }
};

/**
 * Hide the marker's label
 * @param force True to force label to be hidden
 */
API.Marker.Mobile.prototype.hideLabel = function (force) {
    if (this.label_ && (!this.keepInFront || force))
        Ext.get(this.label_).hide();
};

/**
 * Implement this method to remove your elements from the DOM.
 * This method is called once following a call to setMap(null).
 */
API.Marker.Mobile.prototype.onRemove = function () {
    if (this.div_ && this.div_.parentNode) {
        this.hide();
        this.div_.parentNode.removeChild(this.div_);
        this.div_ = null;
    }
};

/**
 * Implement this method to draw or update the overlay.
 * This method is called after onAdd() and when the position from projection.fromLatLngToPixel() would return a new value for a given LatLng.
 * This can happen on change of zoom, center, or map type.
 * It is not necessarily called on drag or resize.
 */
API.Marker.Mobile.prototype.draw = function () {
    if (this.marker.getMap() === null){
        this.marker.setMap(this.getMap());
    }

    if (this.getMap().getZoom() >= API.Map.ZOOM_OUT_REMOVE_LEVEL && this.shrinked) {
        this.unShrink();
    } else if(this.getMap().getZoom() < API.Map.ZOOM_OUT_REMOVE_LEVEL) {
        this.shrink();
    }

    this.setPosition();
    this.rotate(this.opt.heading);
};

/**
 * obter a latitude
 * @return float (latitude do marker em graus decimais)
 */
API.Marker.Mobile.prototype.lat = function () {
    return this.opt.lat;
};

/**
 * obter a longitude
 * @return float (latitude do marker em graus decimais)
 */
API.Marker.Mobile.prototype.lng = function () {
    return this.opt.lng;
};

/**
 * obter o object google.maps.LatLng
 * @return object (google.maps.LatLng)
 */
API.Marker.Mobile.prototype.latLng = function () {
    return this.getPosition();
};

/**
 * Will transform the marker object in it's shrinked shape
 */
API.Marker.Mobile.prototype.shrink = function () {
    //check if marker it's already shrinked

    if (!this.shrinked){
        this.onRemove();
        this.div_ = null;
    }

    //check if the marker object exists
    if (!this.div_) {
        this.div_ = document.createElement("div");

        //set style
        this.div_.className = 'marker-pane-container-shrinked';

        //merge license plate values and show the last 4 chars
        var alias = this.opt.alias.replace(/-/gi, '');
        alias = alias.substring(alias.length - 4, alias.length);

        //set the DIV object
        this.container = this.shape.small.append(this.div_, {
            id: this.opt.id,
            title: this.opt.alias,
            url: this.opt.imgUrl + '?id=' + this.opt.iconid,
            alias: alias,
            txt_visibility: this.isIconVisible ? 'hidden' : 'visible',
            img_visibility: this.isIconVisible ? 'visible' : 'hidden'
        });

        //add DIV overlay to map pane
        this.getPanes().overlayLayer.appendChild(this.div_);
    }
    //set flag
    this.shrinked = true;

    //update position
    this.setPosition();
    //set the overlay rotation pisition
    this.rotate(this.opt.heading);

    //check if label is set
    if (this.label_) {
        //update label object to the shrink marker
        Ext.get('mkr-label-ball-' + this.opt.id).addClass('marker-label-ball-shrinked');
        Ext.get('mkr-label-center-' + this.opt.id).addClass('marker-label-center-shrinked');
    }
    //check if marker is set to be on front
    if (this.keepInFront)
        this.toFront();
};

/**
 * Will transform the marker object in it's normal shape (unshrinked)
 */
API.Marker.Mobile.prototype.unShrink = function () {
    //check if marker it's shrinked
    if(this.shrinked) {
        this.onRemove();
        this.div_ = null;
    }

    if (!this.div_) {
        //create DOM element
        this.div_ = document.createElement("div");

        //set style
        this.div_.className = 'marker-pane-container';

        //merge license plate values and show the last 4 chars
        var alias = this.opt.alias.replace(/-/gi, '');
        alias = alias.substring(alias.length - 4, alias.length);

        //set the DIV object
        this.container = this.shape.normal.append(this.div_, {
            id: this.opt.id,
            title: this.opt.alias,
            url: this.opt.imgUrl + '?id=' + this.opt.iconid,
            alias: alias,
            txt_visibility: this.isIconVisible ? 'hidden' : 'visible',
            img_visibility: this.isIconVisible ? 'visible' : 'hidden'
        });

        //add DIV overlay to map pane
        this.getPanes().overlayLayer.appendChild(this.div_);
    }
    //reset flag
    this.shrinked = false;

    //update position
    this.setPosition();
    //set the overlay rotation pisition
    this.rotate(this.opt.heading);

    //check if label is set
    if (this.label_) {
        //update label object to the normal marker
        Ext.get('mkr-label-ball-' + this.opt.id).removeClass('marker-label-ball-shrinked');
        Ext.get('mkr-label-center-' + this.opt.id).removeClass('marker-label-center-shrinked');
    }
    //check if marker is set to be on front
    if (this.keepInFront)
        this.toFront();
};


/**
 * Hide marker from map
 */
API.Marker.Mobile.prototype.hide = function () {
};

/**
 * Hide marker from map
 */
API.Marker.Mobile.prototype.show = function () {
};

/**
 * Update the marker object with the passed parameters
 */
API.Marker.Mobile.prototype.update = function (options) {

    var map = this.getMap();
    if(map !== null){
        if (map.getZoom() >= API.Map.ZOOM_OUT_REMOVE_LEVEL && this.shrinked) {
            this.unShrink();
        } else if (this.getMap().getZoom() < API.Map.ZOOM_OUT_REMOVE_LEVEL && !this.shrinked) {
            this.shrink();
        }
    }
    //save old icon id
    var oldIconid = this.opt.iconid;
    var newLatLng = false;

    //overrid the object option
    for (k in options) {
        if (k == 'lat' && this.opt[k] != options[k]) {
            this.opt[k] = options[k];
            newLatLng = true;
        } else if (k == 'lng' && this.opt[k] != options[k]) {
            this.opt[k] = options[k];
            newLatLng = true;
        } else {
            this.opt[k] = options[k];
        }
    }
    if (newLatLng)
        this.set('position', new google.maps.LatLng(this.opt['lat'], this.opt['lng']));

    //update position
    this.setPosition();
    this.marker.setMap(this.getMap());

    //update rotation angle
    this.rotate(this.opt.heading);

    //update image
    if (oldIconid != this.opt.iconid)
        $(this.container).attr('src', this.opt.imgUrl + '?id=' + this.opt.iconid);
};

/**
 * Update the marker position
 */
API.Marker.Mobile.prototype.setPosition = function () {
    //define as coordenadas do overlay
    var latlng = this.getPosition();
    //check if thr map projection is set
    if (this.div_ != null && this.getProjection() !== null && this.getProjection() !== undefined) {
        //Computes the pixel coordinates of the given geographical location in the DOM element that holds the map
        var point = this.getProjection().fromLatLngToDivPixel(latlng);
        //update the click area position
        if (this.marker)
            this.marker.setPosition(latlng);
        //check if it's on the shrinked shape
        if (!this.shrinked) {
            this.div_.style.left = (point.x - 28) + "px"; //left
            this.div_.style.top = (point.y - 28) + "px"; //top
        }
        //check if it's on the normal shape
        else {
            this.div_.style.left = (point.x - 18) + "px"; //left
            this.div_.style.top = (point.y - 18) + "px"; //top
        }
        //update label's position
        this.setLabelPosition();
    }

};

/**
 * Rotates the image pointer
 * @param heading The marker rotation angle in degrees
 */
API.Marker.Mobile.prototype.rotate = function (heading) {
    //the the marker pane
    var pane = !this.shrinked ? Ext.get("mkr-pane-" + this.opt.id) : Ext.get("mkr-pane-shrinked-" + this.opt.id);
    //check if pane is set
    if (pane) {
        //check if the mobile is not moving, if true remove the rotation angle and set the "stoped" marker style
        if (!this.opt.moving) {
            //check if browser is the IE
            if (Ext.isIE) {
                //add the stoped css IE style to pane
                pane.addClass(!this.shrinked ? 'marker-pane-stoped-ie' : 'marker-pane-shrinked-stoped-ie');
                //remove rotation angle
                pane.setStyle({
                    'filter': 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)' //IE
                });
            }
            else {
                //add the stoped css style to pane
                pane.addClass(!this.shrinked ? 'marker-pane-stoped' : 'marker-pane-shrinked-stoped');
                //remove rotation angle
                pane.setStyle({
                    '-o-transform': 'rotate(0deg)', //opera
                    'transform': 'rotate(0deg)',  //safari
                    '-webkit-transform': 'rotate(0deg)', //chrome
                    '-moz-transform': 'rotate(0deg)' //firefox
                });
            }
        }
        //if the mobile is moving, update the rotation angle
        //to the IE browser...
        else if (Ext.isIE) {
            /*
             The rotation in the IE accepts the following values:
             0 => 0º
             1 => 90º
             2 => 180º
             3 => 270º
             */
            var c = heading;
            if (c < 0) c = 360 - c; //invert
            if (c <= 45) c = 0;
            else if (c <= 135) c = 1;
            else if (c <= 225) c = 2;
            else if (c <= 315) c = 3;
            else c = 0;
            //set the image rotation using the Microsoft DXImageTransform
            pane.setStyle({
                'filter': 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + c + ')' //IE
            });
        }
        //to all other browsers...
        else {
            //remove stoped classes
            if (Ext.isIE)
                pane.removeClass(['marker-pane-stoped-ie', 'marker-pane-shrinked-stoped-ie']);
            else
                pane.removeClass(['marker-pane-stoped', 'marker-pane-shrinked-stoped']);
            //set rotation values
            pane.setStyle({
                '-o-transform': 'rotate(' + heading + 'deg)', //opera
                'transform': 'rotate(' + heading + 'deg)',  //safari
                '-webkit-transform': 'rotate(' + heading + 'deg)', //chrome
                '-moz-transform': 'rotate(' + heading + 'deg)' //firefox
            });
        }
    }
};

/**
 * Get the visibility status of the marker in the map
 * @return boolean True if the marker is visible
 */
API.Marker.Mobile.prototype.getVisible = function () {
    return this.hidden ? false : true;
};

/**
 * Force marker to be highlight
 */
API.Marker.Mobile.prototype.highlight = function () {
    this.keepInFront = true;
    this.toFront();
    this.showLabel();
};

/**
 * Remove the forced highlight from marker
 */
API.Marker.Mobile.prototype.removeHighlight = function () {
    this.keepInFront = false;
    this.hideLabel();
    this.toBack();
};

API.Marker.Mobile.prototype.toggleIconToAlias = function () {
    if (this.isIconVisible) {

        this.isIconVisible = false;

        if (Ext.get("mkr-pane-shrinked-img-" + this.opt.id))
            Ext.get("mkr-pane-shrinked-img-" + this.opt.id).hide();
        if (Ext.get("mkr-pane-img-" + this.opt.id))
            Ext.get("mkr-pane-img-" + this.opt.id).hide();
        if (Ext.get("mkr-pane-shrinked-txt-" + this.opt.id))
            Ext.get("mkr-pane-shrinked-txt-" + this.opt.id).show();
        if (Ext.get("mkr-pane-txt-" + this.opt.id))
            Ext.get("mkr-pane-txt-" + this.opt.id).show();
    } else {
        this.isIconVisible = true;

        if (Ext.get("mkr-pane-shrinked-txt-" + this.opt.id))
            Ext.get("mkr-pane-shrinked-txt-" + this.opt.id).hide();
        if (Ext.get("mkr-pane-txt-" + this.opt.id))
            Ext.get("mkr-pane-txt-" + this.opt.id).hide();
        if (Ext.get("mkr-pane-shrinked-img-" + this.opt.id))
            Ext.get("mkr-pane-shrinked-img-" + this.opt.id).show();
        if (Ext.get("mkr-pane-img-" + this.opt.id))
            Ext.get("mkr-pane-img-" + this.opt.id).show();
    }
};

/**
 * Get the marker latLng value
 * @return google.maps.LatLng object
 */
API.Marker.Mobile.prototype.getPosition = function () {
    return new google.maps.LatLng(this.opt.lat, this.opt.lng);
};
